import sys,os,uuid,re,subprocess,copy
import cv2
from PIL import Image
from math import ceil,floor
import imageio

#Gray = 0.1140*R + 0.5870*G + 0.2989*B

try:
    input_file = sys.argv[1]
except:
    input_file = 'face.jpg'
try:
    output_file = sys.argv[2]
except:
    output_file = 'encoded_face.jpg'


def get_gray_value(image,position):
    g,b,r = cv2.split(image)
    x,y = position
    G = g.item(x,y)
    B = b.item(x,y)
    R = r.item(x,y)
    Gray = 0.1140*R + 0.5870*G + 0.2989*B
    Gray = ceil(Gray)

    return Gray
    

def get_points(BOX):
    top,left,bottom,right = BOX
    height,width = map(abs,[bottom-top,right-left])
    points = [[i+top,j+left] for i in range(height+1) for j in range(width+1)]

    return points

def make_diffBox(diff,imgSize,boxCenter,size=3):
    width,height = imgSize
    size += 1 if not divmod(size,2)[1] else 0
    radius = divmod(size,2)[0]
    center_x,center_y = boxCenter
    dim_x,dim_y = width,height
    dim_z = 3
    shp = (dim_x,dim_y,dim_z)
    diffBox = np.zeros(shp,dtype="uint8")
    for i in range(height):
        for j in range(width):
            for k in range(3):
                diffBox[i][j][k] = diff if all([center_y-radius <= i <= center_y+radius,center_x-radius <= j <= center_x+radius]) else 0

    return diffBox

def add_diffBox(image,diff,position,size):
    imgSize = [image.shape[0],image.shape[1]]
    diffBox = make_diffBox(diff,imgSize,position,size)
    newImage = image+diffBox
    return newImage
    
def make_diffCross(diff,size=3):
    size += 1 if not divmod(size,2)[1] else 0
    dim_x=dim_y = size
    dim_z = 3
    shp = (dim_x,dim_y,dim_z)
    zeroBox = np.zeros(shp,dtype='uint8')
    diffBox = zeroBox
    center = [divmod(size,2)[0] for size in list(zeroBox.shape)]
    for i in range(size):
        for j in range(size):
            for k in range(3):
                diffBox[i][j][k] = diff if any([i==center[0],j==center[1]]) else 0

    return diffBox

def mapping_cross(mapSize,diff,size,crossCenter):
    width,height = mapSize
    diffCross = make_diffCross(diff,size)
    dim_x,dim_y = width,height
    dim_z = 3
    shp = (dim_x,dim_y,dim_z)
    diffBox = np.zeros(shp,dtype="uint8")
    for i in range(



def add_diffBox(image,diffBox):
    b,g,r = cv2.split(image)
    bd,gd,rd = cv2.split(diffBox)
    for 


def make_diffCross_revised(diff,imgSize,crossCenter,size=3):
    width,height = imgSize
    size += 1 if not divmod(size,2)[1] else 0
    radius = divmod(size,2)[0]
    center_x,center_y = crossCenter
    dim_x,dim_y = width,height
    dim_z = 3
    shp = (dim_x,dim_y,dim_z)
    diffBox = np.zeros(tuple(reversed(shp)),dtype="uint8")
    #center = [divmod(size,2)[0] for size in list(zeroBox.shape)]
    for i in range(3):
        for j in range(size):
            for k in range(size):
                #diffBox[i][j][k] = diff if any([j==center[0],k==center[1]]) else 0
                diffBox[i][j][k] = diff if any([center_y-radius = j == center_y+radius,center_x-radius == k == center_x + radius ]) else 0

    return diffBox

def make_checkpoint(image,BOX):
    top,left,bottom,right = BOX
    height,width = map(abs,[bottom-top,right-left])
    checkpoints = [[top,left],[top,right],[bottom,left],[bottom,right]]
    zeroimage = np.zeros((3,width,height))
    diffBox = make_diffBox(20)

def merge(image1,image2,BOX,coreSize=5):
    top,left,bottom,right = BOX
    #window1 = image1[top:bottom,left:right]
    #window2 = image2[top:bottom,left:right]
    row,col,channels = window1.shape
    result = np.zeros((row,col,channels))
    #[b_mean1,g_mean1,r_mean1],[b_stdDev1,g_stdDev1,r_stdDev1] = cv2.meanStdDev(window1)
    #[b_mean2,g_mean2,r_mean2],[b_stdDev2,g_stdDev2,r_stdDev2] = cv2.meanStdDev(window2)
    height,width = map(abs,[bottom-top,right-left])
    for i in range(height):
        for j in range(width):
            up = i - coreSize if i-coreSize>0 else 0
            down = i + coreSize if i+coreSize<row else row
            left = j - coreSize if j-coreSize>0 else 0
            right = j + coreSize if j+coreSize<col else col
            window1 = image1[up:down,left:right]
            mean,stdDev = cv2.meanStdDev(window1)


def get_channel_values(channel,points):
    def get_value(point):
        x,y = point
        return channel.item(x,y)

    values = list(map(get_value, points))

    return values

def get_standard_deviation(image):
    mean,stdDev = cv2.meanStdDev(image)
    try:
        _,_,channels = image.shape
        mean_whole = sum(mean)/channels
    except:
        mean_whole = mean

    return stdDev

def get_mac():
    def mac_parser(mac):
        mac = mac.upper()
        MAC = ':'.join(re.findall(r'\w{1,2}',mac))
        return MAC
    node = uuid.getnode()
    mac = uuid.UUID(int=node).hex[-12:]
    MAC = mac_parser(mac)

    with open('mac.txt','r') as f:
        mac = f.readlines()[0].strip()

    cmd_to_get_mac ='cat /sys/class/net/eth0/address' 
    process_to_get_mac = os.popen(cmd_to_get_mac)
    MAC = process_to_get_mac.read().strip()
    process_to_get_mac.close()

    MAC = str(subprocess.check_output('cat /sys/class/net/eth0/address',shell=True).strip(),'utf-8')

    MAC = subprocess.check_output('cat /sys/class/net/eth0/address',shell=True).strip().decode()

    return MAC


def encode_mac(mac):
    encoded_mac = mac.split(':')

    return encoded_mac

def encode_face(media,encoded_mac):
    def encode_single(info):
        #g = floor(hex2dec(info)/15)
        #b = hex2dec(info)%15
        g,b = divmod(hex2dec(info),15)
        r = hex2dec(info)//16
        return [g,b,r]

    height,width,_ = media.shape
    center = get_center(media)
    xp,yp = center
    TL = [xp-10,yp-10]
    TC = [xp-10,yp]
    TR = [xp-10,yp+10]
    BL = [xp+10,yp-10]
    BC = [xp+10,yp]
    BR = [xp+10,yp+10]
    encode_points = [TL,TC,TR,BL,BC,BR]

    encoded_mac = [encode_single(info) for info in encoded_mac]
    tl,tc,tr,bl,bc,br = encoded_mac
    for index_point in range(len(encode_points)):
        for channel in range(0,3):
            x,y = [encode_points[index_point][0],encode_points[index_point][1]]
            coding_value = encoded_mac[index_point][channel]
            if index_point == 0:
                ck_x,ck_y = [x+1,y]
            elif index_point == 1:
                ck_x,ck_y = [x+1,y]
            elif index_point == 2:
                ck_x,ck_y = [x+1,y]
            elif index_point == 3:
                ck_x,ck_y = [x-1,y]
            elif index_point == 4:
                ck_x,ck_y = [x-1,y]
            elif index_point == 5:
                ck_x,ck_y = [x-1,y]
            ck_v = media.item(ck_x,ck_y,channel)
            #print(ck_x,ck_y,channel,ck_v)
            if ck_v < 128:
                coded_value = ck_v + coding_value
            else:
                coded_value = ck_v - coding_value
            media.itemset((x,y,channel),coded_value)

    encoded_face = media

    return encoded_face

def decode_face(media):
    def decode_single(info):
        g,b= info
        mac_info_single = g*15+b
        return mac_info_single

    height,width,_ = media.shape
    center = get_center(media)
    xp,yp = center
    TL = [xp-10,yp-10]
    TC = [xp-10,yp]
    TR = [xp-10,yp+10]
    BL = [xp+10,yp-10]
    BC = [xp+10,yp]
    BR = [xp+10,yp+10]
    encode_points = [TL,TC,TR,BL,BC,BR]
    decoded_info = []
    check_info = []
    proofed_info = []
    for index_point in range(len(encode_points)):
        point_info = []
        for channel in range(0,3):
            x,y = [encode_points[index_point][0],encode_points[index_point][1]]
            if index_point == 0:
                ck_x,ck_y = [x+1,y]
            elif index_point == 1:
                ck_x,ck_y = [x+1,y]
            elif index_point == 2:
                ck_x,ck_y = [x+1,y]
            elif index_point == 3:
                ck_x,ck_y = [x-1,y]
            elif index_point == 4:
                ck_x,ck_y = [x-1,y]
            elif index_point == 5:
                ck_x,ck_y = [x-1,y]
            ck_v = media.item(ck_x,ck_y,channel)
            coded_value = media.item(x,y,channel)
            if channel < 2:
                if ck_v < 128:
                    coding_value = coded_value - ck_v
                else:
                    coding_value = ck_v - coded_value
                point_info.append(coding_value)
            else:
                if ck_v < 128:
                    proofing_value = coded_value - ck_v
                else:
                    proofing_value = ck_v - coded_value
                proofed_info.append(proofing_value)
        point_info = decode_single(point_info)
        check_info.append(point_info)
        single_info = dec2hex(str(point_info))
        if len(single_info) < 2:
            single_info = ''.join(['0',single_info])
        decoded_info.append(single_info)

    for index_info in range(0,len(proofed_info)):
        if (check_info[index_info] - proofed_info[index_info] * 16) > 16:
            return []
    decoded_info = '-'.join(decoded_info)
    
    return decoded_info
    
        

def get_center(img):
    height,width,_ = img.shape
    center = [ceil(height/2),ceil(width/2)]

    return center

def hex2dec(string_hex):

    return int(string_hex.upper(),16)

def dec2hex(string_dec):

    return hex(int(string_dec)).split('0x')[-1].upper()

def calculate_mean(img):
    height,width,channels = img.shape
    pixels = height*width*channels
    whole = 0
    for index_h in range(height):
        for index_w in range(width):
            for index_c in range(channels):
                whole += img[index_h][index_w][index_c]
    mean = whole/pixels

    return mean

if __name__ == '__main__':
    media = cv2.imread(input_file)
    center = get_center(media)
    mac = get_mac()
    print("MAC to encode:\t",mac)

    encoded_mac = encode_mac(mac)
    tl,bl,tr,br,le,re = encoded_mac
    mean = calculate_mean(media)
    encoded_face = encode_face(media,encoded_mac)

    #cv2.imwrite(output_file,encoded_face,[int(cv2.IMWRITE_JPEG_QUALITY),100])
    #img = Image.fromarray(encoded_face)
    #img.save(output_file)
    imageio.imwrite(output_file,encoded_face[:,:,::-1],format='png')

    encoded_face = cv2.imread(output_file)
    decoded_mac = decode_face(encoded_face)
    print("MAC decoded:\t",decoded_mac)
